/*
 * Copyright 2017 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.drools.workbench.screens.guided.dtable.client.wizard.table.pages;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.MultiSelectionModel;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SingleSelectionModel;
import org.drools.workbench.models.datamodel.rule.BaseSingleFieldConstraint;
import org.drools.workbench.models.guided.dtable.shared.model.ConditionCol52;
import org.drools.workbench.models.guided.dtable.shared.model.DTCellValue52;
import org.drools.workbench.models.guided.dtable.shared.model.GuidedDecisionTable52;
import org.drools.workbench.models.guided.dtable.shared.model.LimitedEntryConditionCol52;
import org.drools.workbench.models.guided.dtable.shared.model.Pattern52;
import org.drools.workbench.screens.guided.dtable.client.resources.i18n.GuidedDecisionTableConstants;
import org.drools.workbench.screens.guided.dtable.client.widget.DTCellValueWidgetFactory;
import org.drools.workbench.screens.guided.dtable.client.widget.Validator;
import org.drools.workbench.screens.guided.dtable.client.widget.table.utilities.CellUtilities;
import org.drools.workbench.screens.guided.dtable.client.wizard.table.pages.cells.AvailableFieldCell;
import org.drools.workbench.screens.guided.dtable.client.wizard.table.pages.cells.ConditionCell;
import org.drools.workbench.screens.guided.dtable.client.wizard.table.pages.cells.ConditionPatternCell;
import org.drools.workbench.screens.guided.rule.client.editor.CEPOperatorsDropdown;
import org.drools.workbench.screens.guided.rule.client.editor.OperatorSelection;
import org.drools.workbench.screens.guided.rule.client.resources.GuidedRuleEditorResources;
import org.gwtbootstrap3.client.ui.Button;
import org.gwtbootstrap3.client.ui.FormGroup;
import org.gwtbootstrap3.client.ui.HelpBlock;
import org.gwtbootstrap3.client.ui.Label;
import org.gwtbootstrap3.client.ui.Radio;
import org.gwtbootstrap3.client.ui.TextBox;
import org.gwtbootstrap3.client.ui.constants.IconType;
import org.gwtbootstrap3.client.ui.constants.ValidationState;
import org.uberfire.client.callbacks.Callback;
import org.uberfire.ext.widgets.core.client.resources.WizardCellListResources;

/**
 * An implementation of the Fact Patterns Constraints page
 */
@Dependent
public class FactPatternConstraintsPageViewImpl extends Composite
        implements
        FactPatternConstraintsPageView {

    private static FactPatternConstraintsPageWidgetBinder uiBinder = GWT.create(FactPatternConstraintsPageWidgetBinder.class);

    @UiField
    protected ScrollPanel availablePatternsContainer;

    @UiField
    protected ScrollPanel availableFieldsContainer;

    @UiField
    protected ScrollPanel chosenConditionsContainer;

    @UiField
    protected Button btnAdd;

    @UiField
    protected Button btnRemove;

    @UiField
    VerticalPanel conditionDefinition;

    @UiField
    FormGroup calculationType;

    @UiField
    Radio optLiteral;

    @UiField
    Radio optFormula;

    @UiField
    Radio optPredicate;

    @UiField
    TextBox txtColumnHeader;

    @UiField
    HelpBlock txtColumnHeaderIncompleteConditionsHelp;

    @UiField
    HelpBlock txtColumnHeaderNameAlreadyInUseHelp;

    @UiField
    FormGroup columnHeaderContainer;

    @UiField
    TextBox txtPredicateExpression;

    @UiField
    FormGroup predicateExpressionContainer;

    @UiField
    FormGroup operatorContainer;

    @UiField
    SimplePanel ddOperatorContainer;

    @UiField
    HelpBlock ddOperatorContainerHelp;

    @UiField
    TextBox txtValueList;

    @UiField
    VerticalPanel criteriaExtendedEntry;

    @UiField
    VerticalPanel criteriaLimitedEntry;

    @UiField
    FormGroup limitedEntryValueContainer;

    @UiField
    SimplePanel limitedEntryValueWidgetContainer;

    @UiField
    FormGroup defaultValueContainer;

    @UiField
    SimplePanel defaultValueWidgetContainer;

    @UiField(provided = true)
    Button btnMoveUp = new Button() {{
        setIcon(IconType.ANGLE_UP);
    }};

    @UiField(provided = true)
    Button btnMoveDown = new Button() {{
        setIcon(IconType.ANGLE_DOWN);
    }};

    private Presenter presenter;

    private Validator validator;

    private CellUtilities cellUtilities;

    private List<Pattern52> availablePatterns;

    private Pattern52 availablePatternsSelection;

    private MinimumWidthCellList<Pattern52> availablePatternsWidget;

    private Set<AvailableField> availableFieldsSelections;

    private MinimumWidthCellList<AvailableField> availableFieldsWidget;

    private List<ConditionCol52> chosenConditions;

    private ConditionCol52 chosenConditionsSelection;

    private Set<ConditionCol52> chosenConditionsSelections;

    private MinimumWidthCellList<ConditionCol52> chosenConditionsWidget;

    private boolean isOperatorValid;

    private DTCellValueWidgetFactory factory;

    @Inject
    private ConditionPatternCell availableConditionsCell;

    @Inject
    private ConditionCell chosenConditionsCell;

    public FactPatternConstraintsPageViewImpl() {
        initWidget(uiBinder.createAndBindUi(this));
    }

    @PostConstruct
    public void setup() {
        this.availablePatternsWidget = new MinimumWidthCellList<Pattern52>(availableConditionsCell,
                                                                           WizardCellListResources.INSTANCE);
        this.availableFieldsWidget = new MinimumWidthCellList<AvailableField>(new AvailableFieldCell(),
                                                                              WizardCellListResources.INSTANCE);
        this.chosenConditionsWidget = new MinimumWidthCellList<ConditionCol52>(chosenConditionsCell,
                                                                               WizardCellListResources.INSTANCE);
        initialiseAvailablePatterns();
        initialiseAvailableFields();
        initialiseChosenFields();
        initialiseCalculationTypes();
        initialiseColumnHeader();
        initialisePredicateExpression();
        initialiseValueList();
        initialiseShufflers();
    }

    @Override
    public void setValidator(final Validator validator) {
        this.validator = validator;
        this.availableConditionsCell.setValidator(validator);
        this.chosenConditionsCell.setValidator(validator);
    }

    @Override
    public void setDTCellValueUtilities(final CellUtilities cellUtils) {
        this.cellUtilities = cellUtils;
    }

    private void initialiseAvailablePatterns() {
        availablePatternsContainer.add(availablePatternsWidget);
        availablePatternsWidget.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.ENABLED);
        availablePatternsWidget.setMinimumWidth(170);

        final Label lstEmpty = new Label(GuidedDecisionTableConstants.INSTANCE.DecisionTableWizardNoAvailablePatterns());
        lstEmpty.setStyleName(WizardCellListResources.INSTANCE.cellListStyle().cellListEmptyItem());
        availablePatternsWidget.setEmptyListWidget(lstEmpty);

        final SingleSelectionModel<Pattern52> selectionModel = new SingleSelectionModel<Pattern52>();
        availablePatternsWidget.setSelectionModel(selectionModel);

        selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {

            @Override
            public void onSelectionChange(final SelectionChangeEvent event) {
                availablePatternsSelection = selectionModel.getSelectedObject();
                presenter.selectPattern(availablePatternsSelection);
            }
        });
    }

    private void initialiseAvailableFields() {
        availableFieldsContainer.add(availableFieldsWidget);
        availableFieldsWidget.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.ENABLED);
        availableFieldsWidget.setMinimumWidth(170);

        final Label lstEmpty = new Label(GuidedDecisionTableConstants.INSTANCE.DecisionTableWizardNoAvailableFields());
        lstEmpty.setStyleName(WizardCellListResources.INSTANCE.cellListStyle().cellListEmptyItem());
        availableFieldsWidget.setEmptyListWidget(lstEmpty);

        final MultiSelectionModel<AvailableField> selectionModel = new MultiSelectionModel<AvailableField>();
        availableFieldsWidget.setSelectionModel(selectionModel);

        selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {

            @Override
            public void onSelectionChange(final SelectionChangeEvent event) {
                availableFieldsSelections = selectionModel.getSelectedSet();
                btnAdd.setEnabled(availableFieldsSelections.size() > 0);
            }
        });
    }

    private void initialiseChosenFields() {
        chosenConditionsContainer.add(chosenConditionsWidget);
        chosenConditionsWidget.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.ENABLED);
        chosenConditionsWidget.setMinimumWidth(170);

        final Label lstEmpty = new Label(GuidedDecisionTableConstants.INSTANCE.DecisionTableWizardNoChosenFields());
        lstEmpty.setStyleName(WizardCellListResources.INSTANCE.cellListStyle().cellListEmptyItem());
        chosenConditionsWidget.setEmptyListWidget(lstEmpty);

        final MultiSelectionModel<ConditionCol52> selectionModel = new MultiSelectionModel<ConditionCol52>(System::identityHashCode);
        chosenConditionsWidget.setSelectionModel(selectionModel);

        selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {

            @Override
            public void onSelectionChange(final SelectionChangeEvent event) {
                chosenConditionsSelections = new HashSet<ConditionCol52>();
                final Set<ConditionCol52> selections = selectionModel.getSelectedSet();
                for (ConditionCol52 c : selections) {
                    chosenConditionsSelections.add(c);
                }
                chosenConditionsSelected(chosenConditionsSelections);
            }

            private void chosenConditionsSelected(final Set<ConditionCol52> cws) {
                btnRemove.setEnabled(true);
                if (cws.size() == 1) {
                    chosenConditionsSelection = cws.iterator().next();
                    conditionDefinition.setVisible(true);
                    validateConditionHeader();
                    validateConditionOperator();
                    populateConditionDefinition();
                    enableMoveUpButton();
                    enableMoveDownButton();
                } else {
                    chosenConditionsSelection = null;
                    conditionDefinition.setVisible(false);
                    optLiteral.setEnabled(false);
                    optFormula.setEnabled(false);
                    optPredicate.setEnabled(false);
                    txtColumnHeader.setEnabled(false);
                    txtValueList.setEnabled(false);
                    defaultValueContainer.setVisible(false);
                    btnMoveUp.setEnabled(false);
                    btnMoveDown.setEnabled(false);
                }
            }

            private void displayCalculationTypes(final Pattern52 selectedPattern,
                                                 final ConditionCol52 selectedCondition) {
                final boolean isPredicate = (selectedCondition.getConstraintValueType() == BaseSingleFieldConstraint.TYPE_PREDICATE);
                final boolean hasEnum = presenter.hasEnum(selectedPattern,
                                                          selectedCondition);
                calculationType.setVisible(!isPredicate);
                optLiteral.setEnabled(!isPredicate);
                optLiteral.setVisible(!isPredicate);
                optFormula.setEnabled(!(isPredicate || hasEnum));
                optFormula.setVisible(!isPredicate);
                operatorContainer.setVisible(!isPredicate);
                optPredicate.setEnabled(isPredicate);
                optPredicate.setVisible(isPredicate);
                txtPredicateExpression.setEnabled(isPredicate);
                predicateExpressionContainer.setVisible(isPredicate);
            }

            private void populateConditionDefinition() {

                // Fields common to all table formats
                txtColumnHeader.setEnabled(true);
                txtColumnHeader.setText(chosenConditionsSelection.getHeader());

                presenter.getOperatorCompletions(availablePatternsSelection,
                                                 chosenConditionsSelection,
                                                 new Callback<String[]>() {
                                                     @Override
                                                     public void callback(final String[] ops) {
                                                         doPopulateConditionDefinition(ops);
                                                     }
                                                 });
            }

            private void doPopulateConditionDefinition(final String[] ops) {
                final CEPOperatorsDropdown ddOperator = new CEPOperatorsDropdown(ops,
                                                                                 chosenConditionsSelection);

                ddOperator.addPlaceholder(GuidedRuleEditorResources.CONSTANTS.pleaseChoose(),
                                          "");

                ddOperatorContainer.setWidget(ddOperator);

                criteriaExtendedEntry.setVisible(presenter.getTableFormat() == GuidedDecisionTable52.TableFormat.EXTENDED_ENTRY);
                criteriaLimitedEntry.setVisible(presenter.getTableFormat() == GuidedDecisionTable52.TableFormat.LIMITED_ENTRY);

                // Fields specific to the table format
                switch (presenter.getTableFormat()) {
                    case EXTENDED_ENTRY:
                        txtValueList.setEnabled(!presenter.requiresValueList(availablePatternsSelection,
                                                                             chosenConditionsSelection));
                        txtValueList.setText(chosenConditionsSelection.getValueList());

                        makeDefaultValueWidget();
                        defaultValueContainer.setVisible(validator.doesOperatorNeedValue(chosenConditionsSelection));

                        if (chosenConditionsSelection.getConstraintValueType() == BaseSingleFieldConstraint.TYPE_PREDICATE) {
                            txtPredicateExpression.setText(chosenConditionsSelection.getFactField());
                        }

                        ddOperator.addValueChangeHandler(new ValueChangeHandler<OperatorSelection>() {

                            @Override
                            public void onValueChange(ValueChangeEvent<OperatorSelection> event) {
                                chosenConditionsSelection.setOperator(event.getValue().getValue());
                                final boolean requiresValueList = presenter.requiresValueList(availablePatternsSelection,
                                                                                              chosenConditionsSelection);
                                txtValueList.setEnabled(requiresValueList);
                                if (!requiresValueList) {
                                    txtValueList.setText("");
                                } else {
                                    txtValueList.setText(chosenConditionsSelection.getValueList());
                                }
                                presenter.stateChanged();
                                validateConditionOperator();

                                makeDefaultValueWidget();
                                defaultValueContainer.setVisible(validator.doesOperatorNeedValue(chosenConditionsSelection));
                            }
                        });

                        switch (chosenConditionsSelection.getConstraintValueType()) {
                            case BaseSingleFieldConstraint.TYPE_LITERAL:
                                optLiteral.setValue(true);
                                displayCalculationTypes(availablePatternsSelection,
                                                        chosenConditionsSelection);
                                break;
                            case BaseSingleFieldConstraint.TYPE_RET_VALUE:
                                optFormula.setValue(true);
                                displayCalculationTypes(availablePatternsSelection,
                                                        chosenConditionsSelection);
                                break;
                            case BaseSingleFieldConstraint.TYPE_PREDICATE:
                                optPredicate.setValue(true);
                                displayCalculationTypes(availablePatternsSelection,
                                                        chosenConditionsSelection);
                        }
                        break;
                    case LIMITED_ENTRY:
                        calculationType.setVisible(false);
                        makeLimitedValueWidget();

                        // If operator changes the widget used to populate the
                        // value can change
                        ddOperator.addValueChangeHandler(new ValueChangeHandler<OperatorSelection>() {

                            @Override
                            public void onValueChange(ValueChangeEvent<OperatorSelection> event) {
                                chosenConditionsSelection.setOperator(event.getValue().getValue());
                                validateConditionOperator();
                                makeLimitedValueWidget();
                                presenter.stateChanged();
                            }
                        });
                        break;
                }
            }

            private void makeLimitedValueWidget() {
                if (!(chosenConditionsSelection instanceof LimitedEntryConditionCol52)) {
                    return;
                }
                final LimitedEntryConditionCol52 lec = (LimitedEntryConditionCol52) chosenConditionsSelection;
                boolean doesOperatorNeedValue = validator.doesOperatorNeedValue(chosenConditionsSelection);
                if (!doesOperatorNeedValue) {
                    limitedEntryValueContainer.setVisible(false);
                    lec.setValue(null);
                    return;
                }
                limitedEntryValueContainer.setVisible(true);
                if (lec.getValue() == null) {
                    lec.setValue(factory.makeNewValue(chosenConditionsSelection));
                }
                limitedEntryValueWidgetContainer.setWidget(factory.getWidget(availablePatternsSelection,
                                                                             chosenConditionsSelection,
                                                                             lec.getValue()));
            }
        });
    }

    private void makeDefaultValueWidget() {
        DTCellValue52 defaultValue = chosenConditionsSelection.getDefaultValue();
        if (defaultValue == null) {
            defaultValue = factory.makeNewValue(chosenConditionsSelection);
            chosenConditionsSelection.setDefaultValue(defaultValue);
        }

        //Correct comma-separated Default Value if operator does not support it
        if (!validator.doesOperatorAcceptCommaSeparatedValues(chosenConditionsSelection)) {
            cellUtilities.removeCommaSeparatedValue(defaultValue);
        }

        defaultValueWidgetContainer.setWidget(factory.getWidget(availablePatternsSelection,
                                                                chosenConditionsSelection,
                                                                defaultValue));
    }

    private void validateConditionHeader() {
        txtColumnHeaderIncompleteConditionsHelp.setVisible(false);
        txtColumnHeaderNameAlreadyInUseHelp.setVisible(false);

        if (!validator.isConditionHeaderNotBlank(chosenConditionsSelection)) {
            txtColumnHeaderIncompleteConditionsHelp.setVisible(true);
            columnHeaderContainer.addStyleName(ValidationState.ERROR.getCssName());
        } else if (!validator.isConditionHeaderUnique(chosenConditionsSelection)) {
            txtColumnHeaderNameAlreadyInUseHelp.setVisible(true);
            columnHeaderContainer.addStyleName(ValidationState.ERROR.getCssName());
        } else {
            columnHeaderContainer.removeStyleName(ValidationState.ERROR.getCssName());
        }
    }

    private void validateConditionOperator() {
        isOperatorValid = validator.isConditionOperatorValid(chosenConditionsSelection);
        if (isOperatorValid) {
            ddOperatorContainerHelp.setVisible(false);
            operatorContainer.removeStyleName(ValidationState.ERROR.getCssName());
        } else {
            ddOperatorContainerHelp.setVisible(true);
            operatorContainer.addStyleName(ValidationState.ERROR.getCssName());
        }
    }

    private void initialiseCalculationTypes() {
        optLiteral.addClickHandler(new ClickHandler() {

            @Override
            public void onClick(final ClickEvent w) {
                chosenConditionsSelection.setConstraintValueType(BaseSingleFieldConstraint.TYPE_LITERAL);
                chosenConditionsWidget.redraw();
                presenter.assertDefaultValue(availablePatternsSelection,
                                             chosenConditionsSelection);
                makeDefaultValueWidget();
            }
        });

        optFormula.addClickHandler(new ClickHandler() {

            @Override
            public void onClick(final ClickEvent w) {
                chosenConditionsSelection.setConstraintValueType(BaseSingleFieldConstraint.TYPE_RET_VALUE);
                chosenConditionsWidget.redraw();
                presenter.assertDefaultValue(availablePatternsSelection,
                                             chosenConditionsSelection);
                makeDefaultValueWidget();
            }
        });
        optPredicate.addClickHandler(new ClickHandler() {

            @Override
            public void onClick(final ClickEvent w) {
                chosenConditionsSelection.setConstraintValueType(BaseSingleFieldConstraint.TYPE_PREDICATE);
                chosenConditionsWidget.redraw();
                presenter.assertDefaultValue(availablePatternsSelection,
                                             chosenConditionsSelection);
                makeDefaultValueWidget();
            }
        });
    }

    private void initialiseColumnHeader() {
        txtColumnHeader.addValueChangeHandler(new ValueChangeHandler<String>() {

            @Override
            public void onValueChange(final ValueChangeEvent<String> event) {
                final String header = txtColumnHeader.getText();
                chosenConditionsSelection.setHeader(header);
                presenter.stateChanged();
                validateConditionHeader();
            }
        });
    }

    private void initialisePredicateExpression() {
        txtPredicateExpression.addValueChangeHandler(new ValueChangeHandler<String>() {

            @Override
            public void onValueChange(final ValueChangeEvent<String> event) {
                final String expression = txtPredicateExpression.getText();
                chosenConditionsSelection.setFactField(expression);

                // Redraw list widget that shows Predicate expressions
                chosenConditionsWidget.redraw();
            }
        });
    }

    private void initialiseValueList() {

        //Copy value back to model
        txtValueList.addValueChangeHandler(new ValueChangeHandler<String>() {

            @Override
            public void onValueChange(final ValueChangeEvent<String> event) {
                final String valueList = txtValueList.getText();
                chosenConditionsSelection.setValueList(valueList);
                // ValueList is optional, no need to advise of state change
            }
        });

        //Update Default Value widget if necessary
        txtValueList.addBlurHandler(new BlurHandler() {

            @Override
            public void onBlur(final BlurEvent event) {
                presenter.assertDefaultValue(availablePatternsSelection,
                                             chosenConditionsSelection);
                makeDefaultValueWidget();
            }
        });
    }

    private void initialiseShufflers() {
        btnMoveUp.addClickHandler(new ClickHandler() {

            @Override
            public void onClick(final ClickEvent event) {
                final int index = chosenConditions.indexOf(chosenConditionsSelection);
                final ConditionCol52 c = chosenConditions.remove(index);
                chosenConditions.add(index - 1,
                                     c);
                setChosenConditions(chosenConditions);
                availablePatternsSelection.setChildColumns(chosenConditions);
            }
        });
        btnMoveDown.addClickHandler(new ClickHandler() {

            @Override
            public void onClick(final ClickEvent event) {
                final int index = chosenConditions.indexOf(chosenConditionsSelection);
                final ConditionCol52 c = chosenConditions.remove(index);
                chosenConditions.add(index + 1,
                                     c);
                setChosenConditions(chosenConditions);
                availablePatternsSelection.setChildColumns(chosenConditions);
            }
        });
    }

    private void enableMoveUpButton() {
        if (chosenConditions == null || chosenConditionsSelection == null) {
            btnMoveUp.setEnabled(false);
            return;
        }
        int index = chosenConditions.indexOf(chosenConditionsSelection);
        btnMoveUp.setEnabled(index > 0);
    }

    private void enableMoveDownButton() {
        if (chosenConditions == null || chosenConditionsSelection == null) {
            btnMoveDown.setEnabled(false);
            return;
        }
        int index = chosenConditions.indexOf(chosenConditionsSelection);
        btnMoveDown.setEnabled(index < chosenConditions.size() - 1);
    }

    @Override
    public void init(final FactPatternConstraintsPageView.Presenter presenter) {
        this.presenter = presenter;
    }

    @Override
    public void setDTCellValueWidgetFactory(final DTCellValueWidgetFactory factory) {
        this.factory = factory;
    }

    @Override
    public void setAreConditionsDefined(final boolean areConditionsDefined) {
        chosenConditionsWidget.redraw();
        availablePatternsWidget.redraw();
    }

    @Override
    public void setArePatternBindingsUnique(final boolean arePatternBindingsUnique) {
        availablePatternsWidget.redraw();
    }

    @Override
    public void setAvailablePatterns(final List<Pattern52> patterns) {
        availablePatterns = patterns;
        availablePatternsWidget.setRowCount(availablePatterns.size(),
                                            true);
        availablePatternsWidget.setRowData(availablePatterns);

        if (availablePatternsSelection != null) {

            // If the currently selected pattern is no longer available clear selections
            if (!availablePatterns.contains(availablePatternsSelection)) {
                availablePatternsWidget.getSelectionModel().setSelected(availablePatternsSelection,
                                                                        false);
                availablePatternsSelection = null;
                setAvailableFields(new ArrayList<AvailableField>());
                availableFieldsSelections = null;
                setChosenConditions(new ArrayList<ConditionCol52>());
                chosenConditionsSelection = null;
                conditionDefinition.setVisible(false);
            }
        } else {

            // If no available pattern is selected clear fields
            setAvailableFields(new ArrayList<AvailableField>());
            setChosenConditions(new ArrayList<ConditionCol52>());
        }
    }

    @Override
    public void setAvailableFields(final List<AvailableField> fields) {
        availableFieldsWidget.setRowCount(fields.size(),
                                          true);
        availableFieldsWidget.setRowData(fields);
    }

    @Override
    public void setChosenConditions(final List<ConditionCol52> conditions) {
        chosenConditions = conditions;
        chosenConditionsWidget.setRowCount(conditions.size(),
                                           true);
        chosenConditionsWidget.setRowData(conditions);
        conditionDefinition.setVisible(conditions.contains(chosenConditionsSelection));
        enableMoveUpButton();
        enableMoveDownButton();
        presenter.stateChanged();
    }

    @UiHandler(value = "btnAdd")
    public void btnAddClick(final ClickEvent event) {
        for (AvailableField f : availableFieldsSelections) {
            chosenConditions.add(makeNewConditionColumn(f));
        }
        setChosenConditions(chosenConditions);
        availablePatternsSelection.setChildColumns(chosenConditions);
        presenter.stateChanged();
    }

    private ConditionCol52 makeNewConditionColumn(final AvailableField f) {
        final GuidedDecisionTable52.TableFormat format = presenter.getTableFormat();
        if (format == GuidedDecisionTable52.TableFormat.EXTENDED_ENTRY) {
            final ConditionCol52 c = new ConditionCol52();
            c.setFactField(f.getName());
            c.setFieldType(f.getType());
            c.setConstraintValueType(f.getCalculationType());
            return c;
        } else {
            final LimitedEntryConditionCol52 c = new LimitedEntryConditionCol52();
            c.setFactField(f.getName());
            c.setFieldType(f.getType());
            c.setConstraintValueType(BaseSingleFieldConstraint.TYPE_LITERAL);
            return c;
        }
    }

    @UiHandler(value = "btnRemove")
    public void btnRemoveClick(final ClickEvent event) {
        //Don't allow removal if Pattern is used elsewhere and we're removing all constraints
        if (chosenConditions.size() == chosenConditionsSelections.size()) {
            if (!validator.canPatternBeRemoved(availablePatternsSelection)) {
                if (chosenConditions.size() == 1) {
                    Window.alert(GuidedDecisionTableConstants.INSTANCE.UnableToDeleteConditionColumn0(chosenConditions.get(0).getHeader()));
                } else {
                    Window.alert(GuidedDecisionTableConstants.INSTANCE.UnableToDeleteConditionColumns());
                }
                return;
            }
        }

        //Otherwise remove constraints
        for (ConditionCol52 c : chosenConditionsSelections) {
            chosenConditions.remove(c);
        }
        chosenConditionsSelections.clear();
        setChosenConditions(chosenConditions);
        availablePatternsSelection.setChildColumns(chosenConditions);
        presenter.stateChanged();

        txtColumnHeader.setText("");
        txtValueList.setText("");
        defaultValueContainer.setVisible(false);
        conditionDefinition.setVisible(false);
        btnRemove.setEnabled(false);
    }

    interface FactPatternConstraintsPageWidgetBinder
            extends
            UiBinder<Widget, FactPatternConstraintsPageViewImpl> {

    }
}
